/*
// Licensed to Julian Hyde under one or more contributor license
// agreements. See the NOTICE file distributed with this work for
// additional information regarding copyright ownership.
//
// Julian Hyde licenses this file to you under the Apache License,
// Version 2.0 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
 */
package org.olap4j.mdx;

import org.olap4j.Axis;
import org.olap4j.type.Type;

import java.io.PrintWriter;
import java.util.Collections;
import java.util.List;

/**
 * An axis in an MDX query. For example, the typical MDX query has two axes, which appear as the "ON COLUMNS" and
 * "ON ROWS" clauses.
 */
public class AxisNode implements ParseTreeNode {

	private final ParseRegion region;
	private boolean nonEmpty;
	private ParseTreeNode expression;
	private final Axis axis;

	private final List<IdentifierNode> dimensionProperties;

	/**
	 * Creates an axis.
	 * 
	 * @param region
	 *            Region of source code
	 * @param nonEmpty
	 *            Whether to filter out members of this axis whose cells are all empty
	 * @param axis
	 *            Which axis (ROWS, COLUMNS, etc.)
	 * @param dimensionProperties
	 *            List of dimension properties; if null, empty list is assumed
	 * @param expression
	 *            Expression to populate the axis
	 */
	public AxisNode(ParseRegion region, boolean nonEmpty, Axis axis, List<IdentifierNode> dimensionProperties,
			ParseTreeNode expression) {
		this.region = region;
		this.nonEmpty = nonEmpty;
		this.expression = expression;
		this.axis = axis;
		if (axis == null) {
			throw new IllegalArgumentException("Axis type must not be null");
		}
		if (dimensionProperties == null) {
			dimensionProperties = Collections.emptyList();
		}
		this.dimensionProperties = dimensionProperties;
	}

	public ParseRegion getRegion() {
		return region;
	}

	public <T> T accept(ParseTreeVisitor<T> visitor) {
		final T o = visitor.visit(this);

		// visit the expression which forms the axis
		expression.accept(visitor);

		return o;
	}

	/**
	 * Returns the name of the axis this axis expression is populating.
	 * 
	 * @return axis name
	 */
	public Axis getAxis() {
		return axis;
	}

	/**
	 * Returns whether the axis has the <code>NON EMPTY</code> property set.
	 * 
	 * @return whether the axis is NON EMPTY
	 */
	public boolean isNonEmpty() {
		return nonEmpty;
	}

	/**
	 * Sets whether the axis has the <code>NON EMPTY</code> property set.
	 * 
	 * See {@link #isNonEmpty()}.
	 * 
	 * @param nonEmpty
	 *            whether the axis is NON EMPTY
	 */
	public void setNonEmpty(boolean nonEmpty) {
		this.nonEmpty = nonEmpty;
	}

	/**
	 * Returns the expression which is used to compute the value of this axis.
	 * 
	 * @return the expression which is used to compute the value of this axis
	 */
	public ParseTreeNode getExpression() {
		return expression;
	}

	/**
	 * Sets the expression which is used to compute the value of this axis. See {@link #getExpression()}.
	 * 
	 * @param expr
	 *            the expression which is used to compute the value of this axis
	 */
	public void setExpression(ParseTreeNode expr) {
		this.expression = expr;
	}

	public void unparse(ParseTreeWriter writer) {
		PrintWriter pw = writer.getPrintWriter();
		if (nonEmpty) {
			pw.print("NON EMPTY ");
		}
		if (expression != null) {
			expression.unparse(writer);
		}
		if (dimensionProperties.size() > 0) {
			pw.print(" DIMENSION PROPERTIES ");
			for (int i = 0; i < dimensionProperties.size(); i++) {
				IdentifierNode dimensionProperty = dimensionProperties.get(i);
				if (i > 0) {
					pw.print(", ");
				}
				dimensionProperty.unparse(writer);
			}
		}
		if (axis != Axis.FILTER) {
			pw.print(" ON " + axis);
		}
	}

	/**
	 * Returns the list of dimension properties of this axis.
	 * 
	 * @return list of dimension properties
	 */
	public List<IdentifierNode> getDimensionProperties() {
		return dimensionProperties;
	}

	public Type getType() {
		// An axis is not an expression, so does not have a type.
		// Try AxisNode.getExpression().getType() instead.
		return null;
	}

	public AxisNode deepCopy() {
		return new AxisNode(this.region, this.nonEmpty, this.axis, MdxUtil.deepCopyList(dimensionProperties),
				this.expression != null ? this.expression.deepCopy() : null);
	}
}

// End AxisNode.java
